package middleware

import (
	"expvar"
	"fmt"
	"net/http"
	"strconv"
	"strings"
	"sync"
	"time"

	"github.com/felixge/httpsnoop"
	"github.com/tomasen/realip"
	"golang.org/x/time/rate"
	"learning.com/edu/models"
	"learning.com/edu/utils/helper"
	"learning.com/edu/utils/validator"
)

type Middleware struct {
	helper *helper.Helper
	limiterConfig *models.LimiterConfig
	models *models.Models
}

func New(helper *helper.Helper, limiterConfig *models.LimiterConfig, models *models.Models) *Middleware {
	return &Middleware{
		helper: helper,
		limiterConfig: limiterConfig,
		models: models,
	}
}

func (m *Middleware) RequireAuthenticatedUser(next http.HandlerFunc) http.HandlerFunc { 
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		user := m.ContextGetUser(r)
		if user.IsAnonymous() {
			m.helper.AuthenticationRequiredResponse(w, r)
			return
		}
		next.ServeHTTP(w, r)
	})
}

func (m *Middleware) Authenticate(next http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		w.Header().Add("Vary", "Authorization")

		authorizationHeader := r.Header.Get("Authorization")

		if authorizationHeader == "" {
			r = m.ContextSetUser(r, models.AnonymousUser)
			next.ServeHTTP(w, r)
			return
		}

		headerParts := strings.Split(authorizationHeader, " ")
		if len(headerParts) != 2 || headerParts[0] != "Bearer" {
			m.helper.InvalidAuthenticationTokenResponse(w, r)
			return
		}

		token := headerParts[1]
		v := validator.New()
		if models.ValidateTokenPlainToken(v, token); !v.Valid() {
			m.helper.InvalidAuthenticationTokenResponse(w, r)
			return
		}

		user, err := m.models.Users.GetUserForToken(models.ScopeAuthentication, token)
		if err != nil {
			switch {
			case err == helper.ErrRecordNotFound:
				m.helper.InvalidAuthenticationTokenResponse(w, r)
			default:
				m.helper.ServerErrorResponse(w, r, err)
			}
			return
		}

		r = m.ContextSetUser(r, user)
		next.ServeHTTP(w, r)
	})
}

/**
 * LogRequest:处理 HTTP 请求前记录请求信息
 */
func (m *Middleware) LogRequest(next http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		msg := fmt.Sprintf("Request Started: %s - %s %s %s", r.RemoteAddr, r.Proto, r.Method, r.URL.RequestURI())
		m.helper.Logger.Info(msg, nil)
		next.ServeHTTP(w, r)
	})
}

/**
 * RecoverPanic:捕获处理 HTTP 请求时可能发生的恐慌（panic）并进行恢复处理，避免程序崩溃
 */
func (m *Middleware) RecoverPanic(next http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		defer func() {
			if err := recover(); err != nil {
				w.Header().Set("Connection", "close")
				m.helper.ServerErrorResponse(w, r, fmt.Errorf("%s", err))
			}
		}()
		next.ServeHTTP(w, r)
	})
}

/*
 * RateLimit:用于实现基于 IP 地址的请求速率限制功能
 */
func (m *Middleware) RateLimit(next http.Handler) http.Handler {
	// 创建一个 map，用于存储客户端的限速器
	type client struct {
		limiter *rate.Limiter
		lastSeen time.Time
	}

	// 定义变量
	var (
		//使用 sync.Mutex 保证对 clients 映射的并发访问安全
		mu        sync.Mutex
		// clients 映射，键为客户端的 IP 地址，值为对应的限速器
		clients = make(map[string]*client)
	)

	// 定时删除过期的限速器
	go func() {
		time.Sleep(time.Minute)
		mu.Lock()
		for ip, clint := range clients {
			//清理超过 1 分钟未活动的客户端记录，避免内存泄漏
			if time.Since(clint.lastSeen) > time.Minute {
				delete(clients, ip)
			}
		}
		mu.Unlock()
	}()

	// 限速
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 
		// 获取客户端IP
		if m.limiterConfig.Enabled {
			ip := realip.FromRequest(r)
			mu.Lock()
			
			if _, found := clients[ip]; !found {
				rps   := m.limiterConfig.Rps
				burst := m.limiterConfig.Burst
				clients[ip] = &client{
					limiter: rate.NewLimiter(rate.Limit(rps), burst),
				}
			}
			// 检测
			clients[ip].lastSeen = time.Now()
			if !clients[ip].limiter.Allow() {
				mu.Unlock()
				m.helper.RateLimitExceededResponse(w, r)
				return
			}

			mu.Unlock()
		}
		next.ServeHTTP(w, r)
	})
}

/**
 * Metrics：用于收集和统计 HTTP 请求的相关指标
 */
func (m *Middleware) Metrics(next http.Handler) http.Handler { 
	// 接收的总请求数
	totalRequestsReceived := expvar.NewInt("total_requests_received")
	// 发送的总请求数
	totalResponsesSent := expvar.NewInt("total_responses_sent")
	// 总的请求处理时间或响应时间
	totalResponsessingTimeMicroseconds := expvar.NewInt("total_responses_time_us")
	// 状态码统计的响应数
	totalResponsesSentByStatus := expvar.NewMap("total_responses_sent_by_status")

	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		// 每次收到请求时，先将总请求数加 1
		totalRequestsReceived.Add(1)

		// 使用 httpsnoop.CaptureMetrics 调用下一个处理器（next），同时捕获处理 metrics
		metrics := httpsnoop.CaptureMetrics(next, w, r)
		totalResponsesSent.Add(1)
		totalResponsessingTimeMicroseconds.Add(metrics.Duration.Microseconds())

		//处理完成后更新各项统计指标：响应数 + 1、累加响应时间、按状态码记录响应数
		totalResponsesSentByStatus.Add(strconv.Itoa(metrics.Code), 1)
	})
}